/**
 * - 作者: DoooReyn
 * - 日期: 2023.10.30
 */

import { AudioClip, Button, Component } from 'cc';
import { ConstructorOf, Dictionary, Provider } from '../../../../wing/assets/src';
import { ButtonInfo } from '../declarations';

/** 日志组件接口 */
interface LoggerComp extends Component {
    /** 组件名称 */
    get uname(): string;
    /** 组件日志 */
    get logger(): Provider;
}

/** 预注册内容的挂载容器 */
class Injector<DictValue> extends Dictionary<DictValue> {
    /** 修饰符 */
    protected static readonly decorator: string = '__ccc_injector__';

    /**
     * 注册到容器
     * @param ref 引用
     * @returns
     */
    public static register<DV>(ref: LoggerComp): Injector<DV> {
        return ((ref as any)[this.decorator] ??= new this<DV>());
    }

    /** 容器是否已存在 */
    static exists(ref: LoggerComp): boolean {
        return (ref as any)[this.decorator] !== undefined;
    }
}

/** 预注册节点信息挂载容器 */
class Children extends Injector<string> {
    protected static readonly decorator = '__ccc_decorator_children__';
}

/** 预注册组件信息挂载容器 */
export class Components extends Injector<[ConstructorOf<Component>, string]> {
    protected static readonly decorator = '__ccc_decorator_components__';
}

/** 预注册按钮信息挂载容器 */
export class Buttons extends Injector<[ButtonInfo, string]> {
    protected static readonly decorator = '__ccc_decorator_buttons__';
}

/**
 * 装饰器之脚本修饰器
 * @param uname 脚本名称
 * @returns
 */
export function ccmodifier(uname: string) {
    return function <T extends LoggerComp>($target: ConstructorOf<T>) {
        Object.defineProperty($target, '__uname__', { value: uname, writable: false });
    };
}

/**
 * 装饰器之脚本修饰器
 * @param uname 脚本名称
 * @returns
 */
export function modifier(uname: string) {
    return function <T = any>($target: ConstructorOf<T>) {
        Object.defineProperty($target, '__uname__', { value: uname, writable: false });
    };
}

/**
 * 装饰器之节点预注册
 * @param path 节点路径
 * @returns
 */
export function ccchild(path?: string) {
    return function <T extends LoggerComp>($target: T, $key: string) {
        path ??= $key;

        const cache = Children.register<string>($target);
        cache.add($key, path);
        if (cache.size === 1) {
            // @ts-ignore
            const onLoad = $target.onLoad;
            // @ts-ignore
            $target.onLoad = function () {
                cache.each((k, v) => {
                    const child = v === '.' ? this.node : this.node.getChildByPath(v);
                    if (!child) {
                        this.logger.warn(`(${k}) 未找到节点，请确认路径 ${v} 是否正确`);
                    } else {
                        (this as any)[k] = child;
                        this.logger.debug(`(${k}) 注册节点成功！ ${child.name}`);
                    }
                });

                onLoad?.call(this);
            };
        }
    };
}

/**
 * 装饰器之组件预注册
 * @param $path 节点路径
 * @param $comp 组件（类型）
 * @returns
 */
export function cccomponent<T extends Component>($comp: ConstructorOf<T>, $path?: string) {
    return function <T extends LoggerComp>($target: T, $key: string) {
        $path ??= $key;
        const cache = Components.register<[ConstructorOf<Component>, string]>($target);
        cache.add($key, [$comp, $path]);
        if (cache.size === 1) {
            // @ts-ignore
            const onLoad = $target.onLoad;
            // @ts-ignore
            $target.onLoad = function () {
                cache.each((k, v) => {
                    const [comp, path] = v;
                    const child = path === '.' ? this.node : this.node.getChildByPath(path);
                    if (!child) {
                        this.logger.warn(`(${k}) 未找到节点，请确认路径是否正确: ${path}`);
                    } else {
                        const c = child.getComponent(comp);
                        if (!c) {
                            this.logger.warn(`(${k}) 未找到组件 ${comp.name}`);
                        } else {
                            (this as any)[k] = c;
                            this.logger.debug(`(${k}) 注册组件成功！ ${c.name}`);
                        }
                    }
                });

                onLoad?.call(this);
            };
        }
    };
}

/**
 * 装饰器之按钮预注册
 * @param path 节点路径
 * @param info 按钮信息
 * @returns
 */
export function ccbutton(info: ButtonInfo, path?: string) {
    return function <T extends LoggerComp>($target: T, $key: string) {
        path ??= $key;
        const cache = Buttons.register<[ButtonInfo, string]>($target);
        cache.add($key, [info, path]);
        if (cache.size === 1) {
            // @ts-ignore
            const onLoad = $target.onLoad;
            // @ts-ignore
            $target.onLoad = function () {
                cache.each((k, v) => {
                    const [info, path] = v;
                    const child = path === '.' ? this.node : this.node.getChildByPath(path);
                    if (!child) {
                        this.logger.warn(`(${k}) 未找到节点，请确认路径是否正确: ${v}`);
                        return;
                    }

                    let time = Date.now();
                    info.bundle ??= 'resources';
                    info.delay ??= 300;

                    const btn = child.getComponent(Button) || child.addComponent(Button);
                    if (btn.transition === Button.Transition.SCALE) {
                        btn.zoomScale = 0.95;
                        btn.duration = 0.1;
                    }
                    btn.interactable = true;

                    const func = (this as any)[info.func];
                    if (typeof func !== 'function') {
                        this.logger.warn(`(${k}) 未找到方法 ${info.func}`);
                    } else {
                        const onClicked = (e: Button) => {
                            const now = Date.now();
                            if (now - time > info.delay!) {
                                time = now;
                                if (info.sound) {
                                    (window as any).app.audioPlayer.playEffect({
                                        path: info.sound,
                                        bundle: info.bundle,
                                        type: AudioClip,
                                    });
                                }
                                func.call(this, e, info.param);
                            }
                        };
                        child.on(Button.EventType.CLICK, onClicked, this);
                    }

                    (this as any)[k] = btn;
                    this.logger.debug(`(${k}) 注册按钮成功！ ${info.func}`);
                });

                onLoad?.call(this);
            };
        }
    };
}
